﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharePointCommander.Model.PropertySheet;
using System.Collections;
using System.Reflection;

namespace SharePointCommander.Binding
{
    public class BindingHelper
    {
        public static IList<PropertyItem> CreatePropertyItems(Hashtable properties)
        {
            IList<PropertyItem> items = new List<PropertyItem>();
            foreach (string key in properties.Keys.OfType<string>().OrderBy(k => k))
            {
                object value = properties[key];
                if (value is Array)
                {
                    items.Add(new PropertyItemArray(key, (Array)properties[key]) { ValueType = value.GetType().FullName });
                }
                else if (value is Boolean)
                {
                    items.Add(new PropertyItemBoolean(key, (Boolean)properties[key]) { ValueType = value.GetType().FullName });
                }
                else if (value is DateTime)
                {
                    items.Add(new PropertyItemDateTime(key, (DateTime)properties[key]) { ValueType = value.GetType().FullName });
                }
                else if (value is Byte || value is Int16 || value is Int32 || value is Int64)
                {
                    Type intGenType = typeof(PropertyItemInt<>);
                    Type propType = intGenType.MakeGenericType(value.GetType());
                    PropertyItem propertyItem = (PropertyItem)Activator.CreateInstance(propType, key, value);
                    propertyItem.ValueType = value.GetType().FullName;
                    items.Add(propertyItem);
                }
                else if (value is Enum)
                {
                    items.Add(new PropertyItemChoice(key, Enum.GetName(value.GetType(), value)) { ChoicesString = string.Join(",", Enum.GetNames(value.GetType())), ValueType = value.GetType().FullName });
                }
                else
                {
                    items.Add(new PropertyItemString(key, Convert.ToString(properties[key])) { ValueType = value != null ? value.GetType().FullName : "[Null Value]" });
                }
            }
            return items;
        }

        public static void SavePropertyItems(IList<PropertyItem> propertyItems, Hashtable properties)
        {
            foreach (PropertyItem propertyItem in propertyItems)
            {
                Type type = propertyItem.GetType();

                if (propertyItem is PropertyItemArray)
                {
                    properties[propertyItem.Name] = ((PropertyItemArray)propertyItem).Value;
                }
                else if (propertyItem is PropertyItemBoolean)
                {
                    properties[propertyItem.Name] = ((PropertyItemBoolean)propertyItem).Value;
                }
                else if (propertyItem is PropertyItemDateTime)
                {
                    properties[propertyItem.Name] = ((PropertyItemDateTime)propertyItem).Value;
                }
                else if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(PropertyItemInt<>)))
                {
                    properties[propertyItem.Name] = propertyItem.GetType().GetProperty("Name").GetValue(propertyItem, null); 
                }
                else if (propertyItem is PropertyItemChoice)
                {
                    properties[propertyItem.Name] = ((PropertyItemChoice)propertyItem).Value;
                }
                else if (propertyItem is PropertyItemString)
                {
                    properties[propertyItem.Name] = ((PropertyItemString)propertyItem).Value;
                }
            }
        }

        public static IList<PropertyItem> GetObjectFields(object spObject)
        {
            IList<PropertyItem> objectFields = new List<PropertyItem>();

            Type type = spObject.GetType();

            foreach (PropertyInfo propertyInfo in type.GetProperties().Where(p => p.CanRead))
            {
                addField(objectFields, createPropertyItem(propertyInfo, propertyInfo.PropertyType, getPropertyValue(spObject, propertyInfo)));
            }
            foreach (FieldInfo fieldInfo in type.GetFields())
            {
                addField(objectFields, createPropertyItem(fieldInfo, fieldInfo.FieldType, getFieldValue(spObject, fieldInfo)));
            }

            return objectFields.OrderBy(f => f.Name).ToList();
        }

        private static void addField(IList<PropertyItem> props, PropertyItem prop)
        {
            if (prop != null)
            {
                props.Add(prop);
            }
        }

        private static object getPropertyValue(object spObject, PropertyInfo propertyInfo)
        {
            try
            {
                return propertyInfo.GetValue(spObject, null);
            }
            catch (Exception ex)
            {
                return ex;
            }
        }

        private static object getFieldValue(object spObject, FieldInfo fieldInfo)
        {
            try
            {
                return fieldInfo.GetValue(spObject);
            }
            catch (Exception ex)
            {
                return ex;
            }
        }

        private static PropertyItem createPropertyItem(MemberInfo memberInfo, Type type, object value)
        {
            PropertyItem propertyItem = null;
            if (value is Exception)
            {
                propertyItem = new PropertyItemException(memberInfo.Name, (Exception)value) { ValueType = type.FullName };
            }
            else
            {
                if (type.Equals(typeof(string)))
                {
                    propertyItem = new PropertyItemString(memberInfo.Name, (string)value) { ValueType = type.FullName };
                }
                else if (type.Equals(typeof(Byte)) || type.Equals(typeof(Int16)) || type.Equals(typeof(Int32)) || type.Equals(typeof(Int64)))
                {
                    Type intGenType = typeof(PropertyItemInt<>);
                    Type propType = intGenType.MakeGenericType(type);
                    propertyItem = (PropertyItem)Activator.CreateInstance(propType, memberInfo.Name, value);
                }
                else if (type.Equals(typeof(bool)))
                {
                    propertyItem = new PropertyItemBoolean(memberInfo.Name, (bool)value) { ValueType = type.FullName };
                }
                else if (type.IsEnum)
                {
                    propertyItem = new PropertyItemChoice(memberInfo.Name, Enum.GetName(type, value)) { Choices = Enum.GetNames(type), ValueType = type.FullName };
                }
                else if (type.Equals(typeof(DateTime)))
                {
                    propertyItem = new PropertyItemDateTime(memberInfo.Name, (DateTime)value) { ValueType = type.FullName };
                }
                else if (type.IsArray)
                {
                    propertyItem = new PropertyItemArray(memberInfo.Name, (Array)value) { ValueType = type.FullName };
                }
                else if (type.Equals(typeof(Guid)))
                {
                    propertyItem = new PropertyItemGuid(memberInfo.Name, (Guid)value) { ValueType = type.FullName };
                }
                else
                {
                    propertyItem = new PropertyItemObject(memberInfo.Name, value) { ValueType = type.FullName, ReadOnly = true };
                }
                if (memberInfo is PropertyInfo)
                {
                    propertyItem.ReadOnly = !((PropertyInfo)memberInfo).CanWrite;
                }
            }

            return propertyItem;
        }
    }
}
